#include <Resource.h>
#include <ResourceManager.h>
#include <SyscallDispatcher.h>
#include <MonitorDriver.h>
#include <SerialDriver.h>
#include <RemoteResource.h>
#include <ProcessManager.h>

Resource::Resource(Callable *target, const char *name)
  : target(target)
{
  resourceId = resourceManager.create(this, name);
}

Resource::~Resource()
{
  resourceManager.destroy(resourceId);
}

void Resource::add(Call0 call, u32int id)
{
  if (!isMapped(id))
    table0.add(id, call);
}

void Resource::add(Call1 call, u32int id)
{
  if (!isMapped(id))
    table1.add(id, call);
}

void Resource::add(Call2 call, u32int id)
{
  if (!isMapped(id))
    table2.add(id, call);
}

void Resource::add(Call3 call, u32int id)
{
  if (!isMapped(id))
    table3.add(id, call);
}

void Resource::add(Call4 call, u32int id)
{
  if (!isMapped(id))
    table4.add(id, call);
}

void Resource::add(Call5 call, u32int id)
{
  if (!isMapped(id))
    table5.add(id, call);
}
  
void Resource::remove(u32int id)
{
  if (isMapped(id))
  {
    if (table0.remove(id))
      return;
    if (table1.remove(id))
      return;
    if (table2.remove(id))
      return;
    if (table3.remove(id))
      return;
    if (table4.remove(id))
      return;
    if (table5.remove(id))
      return;
  }
}
  
int Resource::callSys(u32int id, bool synchronous, int p1, int p2, int p3, int p4, int p5)
{
  Call0 call0 = table0.lookup(id);
  if (call0)
    return (target->*call0)();
  Call1 call1 = table1.lookup(id);
  if (call1)
    return (target->*call1)(p1);
  Call2 call2 = table2.lookup(id);
  if (call2)
    return (target->*call2)(p1,p2);
  Call3 call3 = table3.lookup(id);
  if (call3)
    return (target->*call3)(p1,p2,p3);
  Call4 call4 = table4.lookup(id);
  if (call4)
    return (target->*call4)(p1,p2,p3,p4);
  Call5 call5 = table5.lookup(id);
  if (call5)
    return (target->*call5)(p1,p2,p3,p4,p5);
  return -1;
}

bool Resource::isMapped(u32int id)
{
  if (table0.lookup(id) || table1.lookup(id) || table2.lookup(id) || table3.lookup(id) ||
      table4.lookup(id) || table5.lookup(id) )
    return true;
  else
    return false;
}

int callResourceUser(u32int rId, u32int fId, int p1, int p2, int p3, int p4)
{
  // Perform a syscall.
  int ret;
  asm volatile("int $50" : "=a" (ret) : "0" (rId), "b" (fId), "c" (p1),
               "d" (p2), "S" (p3), "D" (p4));
  return ret;
}

int callResourceUserAsync(u32int rId, u32int fId, int p1, int p2, int p3, int p4)
{
  // Perform a syscall.
  int ret;
  asm volatile("int $51" : "=a" (ret) : "0" (rId), "b" (fId), "c" (p1),
               "d" (p2), "S" (p3), "D" (p4));
  return ret;
}

int callResourceSys(u32int rId, u32int fId, int p1, int p2, int p3, int p4, int p5)
{
  DEBUG_SPAM("Calling function " << dec << fId << " on resource " << rId << ".");
  Resource *r = resourceManager.request(rId);
  if (r)
  {
    int tmp = r->callSys(fId, true, p1, p2, p3, p4, p5);
    return tmp;
  }
  else
    return -1;
}

int callResourceSysAsync(u32int rId, u32int fId, int p1, int p2, int p3, int p4, int p5)
{
  Resource *r = resourceManager.request(rId);
  if (r)
    return r->callSys(fId, false, p1, p2, p3, p4, p5);
  else
    return -1;
}

u32int requestResource(const char *name)
{
  // Perform a syscall.
  int ret;
  asm volatile("int $30" : "=a" (ret) : "0" (SYSCALL_REQUEST), "b" ((int)name));
  return (u32int)ret;
}

int callResource(u32int rId, const int fId)
{
  return callResourceUser(rId, (u32int)fId);
}

int callResourceAsync(u32int rId, const int fId)
{
  return callResourceUserAsync(rId, (u32int)fId);
}
